package jp.sourceforge.talisman.mds.cluster;

/*
 * $Id: ParallelHardCMeanMethod.java,v 1.1 2009/09/12 05:06:01 weiwei Exp $
 */

import jp.sourceforge.talisman.mds.Item;
import jp.sourceforge.talisman.mds.distance.ItemDistanceCalculator;

/**
 * Parallel hard(Crisp) c-Mean algorithm.
 *
 * @author Haruaki Tamada
 * @version $Revision: 1.1 $ 
 */
class ParallelHardCMeanMethod extends NonHierarchicalClusteringMethod{
    public ParallelHardCMeanMethod(ClusteringParameter param) throws ParameterTypeMismatchException{
        super(param);
        if(!(param instanceof NonHierarchicalClusteringParameter)){
            throw new ParameterTypeMismatchException("NonHierarchicalClusteringParameter is required.");
        }
    }

    public ParallelHardCMeanMethod(NonHierarchicalClusteringParameter param){
        super(param);
    }

    @Override
    protected Cluster[] doClustering(Item[] items, ClusteringParameter initParam){
        NonHierarchicalClusteringParameter param = (NonHierarchicalClusteringParameter)initParam;

        Cluster[] clusters = initClusters(items, param);
        ItemDistanceCalculator calculator = param.getDistanceCalculator();

        boolean flag = true;
        while(flag){
            flag = false;
            Cluster[] nextClusters = new Cluster[clusters.length];
            for(int j = 0; j < clusters.length; j++){
                nextClusters[j] = new Cluster();
            }
            for(int i = 0; i < items.length; i++){
                int current = -1;
                int target = -1;
                double length = Double.MAX_VALUE;
                for(int j = 0; j < clusters.length; j++){
                    if(clusters[j].isContain(items[i])){
                        current = j;
                    }
                    double distance = calculator.calculate(clusters[j].getMedian(), items[i]);
                    if(length > distance){
                        length = distance;
                        target = j;
                    }
                }
                nextClusters[target].addItem(items[i]);
                flag = flag || current != target;
            }
            clusters = nextClusters;

            if(flag){
                fixedNextClusters(clusters);
            }
        }

        return clusters;
    }

    /**
     * If a cluster ALPHA has no elements, this method move 
     * a element from the cluster of largest element size to cluster ALPHA.
     */
    private void fixedNextClusters(Cluster[] clusters){
        for(int i = 0; i < clusters.length; i++){
            if(clusters[i].getSize() == 0){
                int length = 0;
                int target = -1;
                for(int j = 0; j < clusters.length; j++){
                    if(length < clusters[j].getSize()){
                        length = clusters[j].getSize();
                        target = j;
                    }
                }
                clusters[i].addItem(clusters[target].remove(0));
            }
        }
    }
}
